package com.baiwei.vpnapi.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baiwei.vpnapi.dao.*;
import com.baiwei.vpnapi.entity.*;
import com.baiwei.vpnapi.entity.reqentity.BuyPackageInput;
import com.baiwei.vpnapi.entity.respentity.AvailablePackage;
import com.baiwei.vpnapi.entity.respentity.DiscountPackage;
import com.baiwei.vpnapi.exception.BusinessEnum;
import com.baiwei.vpnapi.exception.PaymentException;
import com.baiwei.vpnapi.exception.VpnException;
import com.baiwei.vpnapi.service.VpnService;
import com.baiwei.vpnapi.utils.AppUtils;
import com.baiwei.vpnapi.utils.JwtTokenUtil;
import com.baiwei.vpnapi.utils.crypto.KeyPair;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Array;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @program: vpnapi
 * @description: Vpn服务实现
 * @author: Mr. Stone
 * @create: 2020-02-26 23:23
 **/
@Service
public class VpnServiceImpl implements VpnService {
    @Autowired
    private VpnPackageMapper vpnPackageMapper;

    @Autowired
    private ServerMapper serverMapper;

    @Autowired
    private UserPackageMapper userPackageMapper;

    @Autowired
    private PackageOrderMapper packageOrderMapper;

    @Autowired
    private VpnConnectionMapper vpnConnectionMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private AllowIpMapper allowIpMapper;

    @Autowired
    private HttpServletRequest request;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    private Logger logger = LoggerFactory.getLogger(VpnServiceImpl.class);

    @Override
    public List<DiscountPackage> getAllDiscountPackage() {
        String lang = request.getHeader("language");
        List<DiscountPackage> packages = vpnPackageMapper.getAllDiscountPackage();
        if(packages == null || packages.size() == 0) {
            return null;
        }
        return packages.stream().peek(e -> {
           e.setHint(e.getHintCn());
           if(lang.equals("1")) {
               e.setHint(e.getHintEn());
               e.setPackageName(e.getEnglishName());
           } else if(lang.equals("2")) {
               e.setHint(e.getHintTw());
               e.setPackageName(e.getTaiwanName());
           }
           if (e.getDiscountStatus() == null ||
                   !(e.getDiscountStatus().equals(1))) {
                e.setHint(null);
                e.setDiscount(null);
                e.setDiscountHkPrice(null);
                e.setDiscountPrice(null);
                e.setDiscountStatus(null);
           }
        }).collect(Collectors.toList());

    }

    @Override
    public List<VpnPackage> getPackages() {
        String lang = request.getHeader("language");
        List<VpnPackage> packages = vpnPackageMapper.getAvailablePackage();
        if(packages == null || packages.size() == 0) {
            return null;
        }
        for(VpnPackage pack: packages) {
            if(lang.equals("1")) {
                pack.setPackageName(pack.getEnglishName());
            } else if(lang.equals("2")) {
                pack.setPackageName(pack.getTaiwanName());
            }
        }
        return packages;
    }

    @Override
    public Object buyPackage(BuyPackageInput input) {
        QueryWrapper<VpnPackage> qwv = new QueryWrapper<>();
        qwv.lambda().eq(VpnPackage::getPackageId, input.getPackageId());
        VpnPackage pack = vpnPackageMapper.selectOne(qwv);
        VpnUser user = userMapper.selectById(input.getUserId());
        if(pack == null) {
            throw new PaymentException(BusinessEnum.PAYMENT_PACKAGE_NOT_EXIST);
        }
        logger.info("user: \n{} start buy package: \n{}", user, pack);
        // 创建订单
        String orderNo = AppUtils.generateOrder();
        PackageOrder order = new PackageOrder();
        order.setOrderNo(orderNo);
        // 0 待支付 1 支付成功 2. 失败
        order.setStatus(1);
        order.setCreateTime(new Date());
        order.setMoneyUnit("1");
        order.setPackageId(input.getPackageId());
        order.setUserId(user.getUserId());
        // todo 价格换算待定
        order.setMoney(pack.getPrice());
        order.setPaymentId(1);
        packageOrderMapper.insert(order);
        logger.info("insert order: \n {}", order);
        //插入用户套餐表
        UserPackage qup = new UserPackage();
        qup.setUserId(user.getUserId());
        AvailablePackage tup = userPackageMapper.findLastUnusedOrInusePackage(user.getUserId());
        if(tup == null) {
            //没有在使用的套餐
            long dalta = pack.getDuration().longValue() * 3600 * 24 * 1000;
            long nowsamp = System.currentTimeMillis();
            long endsamp = System.currentTimeMillis() + dalta;
            Date startDate = new Date(nowsamp);
            Date endDate = new Date(endsamp);
            qup.setStartTime(startDate);
            qup.setEndTime(endDate);
            qup.setStatus(1);
            qup.setPackageId(input.getPackageId());
            userPackageMapper.insert(qup);
        } else {
            long dalta = pack.getDuration().longValue() * 3600 * 24 * 1000;
            long nowsamp = tup.getEndTime().getTime();
            long endsamp = nowsamp + dalta;
            qup.setStatus(2);
            qup.setStartTime(new Date(nowsamp));
            qup.setEndTime(new Date(endsamp));
            qup.setPackageId(input.getPackageId());
            userPackageMapper.insert(qup);
        }
        return "购买成功";
    }

    @Override
    public Object connect(Integer serverId) {
        String token = request.getHeader("Authorization");
        String userId = jwtTokenUtil.getUsernameFromToken(token);
        String deviceIdentify = request.getHeader("device");
        logger.info("====> start connect:\n userId: {}\ndevice:{}\nserverId: {}", userId, deviceIdentify, serverId);
        if(deviceIdentify == null || deviceIdentify.length() == 0) {
            throw new VpnException(BusinessEnum.VPN_CONFIG_DEVICE_EMPTY);
        }

        String[] myServerId = jwtTokenUtil.getServerId(token);

        // 检查服务器是否存在，服务器可能在连接的时候被下线
        VpnServer qServer = new VpnServer();
        qServer.setStatus(1);
        qServer.setServerId(serverId);
        VpnServer server = serverMapper.selectOne(new QueryWrapper<>(qServer));
        if(server == null) {
            throw new VpnException(BusinessEnum.VPN_SERVER_UPDATED);
        }

        //查询是否当前设备是否已经连接 如何已经连接线断开这些连接
        VpnConnection qConnect = new VpnConnection();
        qConnect.setStatus(1);
        qConnect.setDeviceIdentifier(deviceIdentify);
        List<VpnConnection> cons = vpnConnectionMapper.selectList(new QueryWrapper<>(qConnect));
        if(cons != null && cons.size() > 0) {
            logger.info("current device has been connected: {}", cons);
            for(VpnConnection con: cons) {
                disconnectOfDevice(con, userId);
            }
        }
        VpnUser user = userMapper.selectById(userId);

        // 专属用户直接连接
        if (myServerId != null && myServerId.length > 0) {
            List<AvailablePackage> exclusivePackages = userPackageMapper.findExclusivePackage(Integer.valueOf(userId));
            if(exclusivePackages == null || exclusivePackages.size() == 0) {
                // 用户专属线路可能已经到期
                throw new VpnException(BusinessEnum.VPN_SERVER_UPDATED);
            }
            VpnServer targetServer = serverMapper.selectById(serverId);
            if(targetServer.getServerType().equals(2)) {
                // 专线用户连接专线
                List<AvailablePackage> collect = exclusivePackages
                        .stream()
                        .filter(e -> e.getServerId().equals(serverId))
                        .collect(Collectors.toList());
                if(collect.size() == 0) {
                    // 用户专属线路可能已经到期
                    throw new VpnException(BusinessEnum.VPN_SERVER_UPDATED);
                }
                return startConnect(deviceIdentify, server, user);
            } else {
                // 专线用户连接vip线路（前面已经判断过确定用户是有有效期内的专属线路）直接连接
                Integer connectCount = vpnConnectionMapper.getConnectCount(new Integer(userId));
                logger.info("user current connect count = {}", connectCount);
                if(5 <= connectCount) {
                    throw new VpnException(BusinessEnum.VPN_CONNECT_COUNT_LIMIT);
                }
                return startConnect(deviceIdentify, server, user);
            }

            // 专线用户连接vip线路
//            List<AvailablePackage> exclusivePackages = userPackageMapper.findExclusivePackage(Integer.valueOf(userId));
//            if(exclusivePackages == null || exclusivePackages.size() == 0) {
//                throw new VpnException(BusinessEnum.VPN_SERVER_UPDATED);
//            }
//            Date now = new Date();
//            if(exclusivePackages.stream()
//                    .noneMatch((p) ->
//                            p.getEndTime().compareTo(now) > 0)) {
//                throw new VpnException(BusinessEnum.VPN_SERVER_UPDATED);
//            }
//            VpnServer targetServer = serverMapper.selectById(serverId);
//            if(Arrays.asList(myServerId).contains(serverId.toString())) {
//                return startConnect(deviceIdentify, server, user);
//            } else {
//                Integer connectCount = vpnConnectionMapper.getConnectCount(new Integer(userId));
//                logger.info("user current connect count = {}", connectCount);
//                if(5 <= connectCount) {
//                    throw new VpnException(BusinessEnum.VPN_CONNECT_COUNT_LIMIT);
//                }
//                return startConnect(deviceIdentify, server, user);
//            }
        }

        // 免费线路直接连接
        if(server.getIsFree() == 1) {
            VpnConnection qFreeCon = new VpnConnection();
            qFreeCon.setIsfree(1);
            qFreeCon.setStatus(1);
            qFreeCon.setUserId(new Integer(userId));
            VpnConnection freeCon = vpnConnectionMapper.selectOne(new QueryWrapper<>(qFreeCon));
            if (freeCon != null) {
                throw new VpnException(BusinessEnum.VPN_FREE_CONNECT_OVERLIMIT);
            }
            return startConnect(deviceIdentify, server, user);
        }

        //查询用户是否有激活的套餐 以及设备数量情况
        AvailablePackage currentPackage = userPackageMapper.findCurrentPakcage(new Integer(userId));
        if(currentPackage == null) {
            logger.info("user has no active package");
            throw new VpnException(BusinessEnum.VPN_NOT_VALID_PACKAGE);
        }
        Integer connectCount = vpnConnectionMapper.getConnectCount(new Integer(userId));
        logger.info("user current connect count = {}", connectCount);
        if(currentPackage.getDeviceCount() <= connectCount) {
            throw new VpnException(BusinessEnum.VPN_CONNECT_COUNT_LIMIT);
        }
        return startConnect(deviceIdentify, server, user);
    }

    /**
    * @Description: 连接步骤 查询allowip-> 生成密钥-> 上传配置到vpn服务器
     * *                  -> 保存连接信息 -> 更新allow_ip状态 -> 保存连接信息 -> 返回配置信息
    * @Param: device 设备标示 server 服务器 user 用户
    * @return:
    * @Author: Mr.Wang
    * @Date: 2020/3/12
    */
    @Transactional
    public Map<String, String> startConnect(String device, VpnServer server, VpnUser user) {
        VpnAllowedip ap = allowIpMapper.selectRandAllowIp(server.getServerId());
        logger.info("start connect\n device: {}\n server: {}\n user: {}\n", device, server, user);
        if(ap == null) {
            throw new VpnException(BusinessEnum.VPN_SERVER_ALLOWEDIP_EMPTY);
        }
        //上传配置到vpn服务器
        KeyPair kp = new KeyPair();
        String pubK = kp.getPublicKey().toBase64();
        String privateK = kp.getPrivateKey().toBase64();
        logger.info("generate keypair\n pk:{}\nsk:{}", pubK, privateK);
        connectToServer(server.getServerIp(),pubK,ap.getIpAddr(), user.getUsername());

        //更新allowIp状态
        ap.setStatus(0);
        allowIpMapper.updateById(ap);
        logger.info("update allowIp to reuse: {}", ap);
        // 保存连接信息
        VpnConnection icon = new VpnConnection();
        icon.setUserId(user.getUserId());
        icon.setPubKey(pubK);
        icon.setPrivateKey(privateK);
        icon.setAllowip(ap.getIpAddr());
        icon.setServerId(server.getServerId());
        icon.setAllowipId(ap.getAllowipId());
        icon.setStatus(1);
        icon.setDeviceIdentifier(device);
        icon.setServerIp(server.getServerIp());
        icon.setIsfree(server.getIsFree());
        vpnConnectionMapper.insert(icon);
        logger.info("insert connect to db: {}", icon);
        Map<String,String> res = new HashMap<>();
        res.put("clientPrivateKey", privateK);
        res.put("serverPublicKey", server.getPubKey());
        res.put("allowIp", ap.getIpAddr());
        res.put("serverIp", server.getServerIp());
        res.put("serverPort", server.getServerPort());
        logger.info("return data: {}", res);
        return res;
    }

    @Transactional
    public void disconnect(String deviceIdentify, String userId) {
        //获取连接信息
        VpnConnection qCon = new VpnConnection();
        qCon.setStatus(1);
        qCon.setDeviceIdentifier(deviceIdentify);
        VpnConnection tCon = vpnConnectionMapper.selectOne(new QueryWrapper<>(qCon));
        if(null != tCon) {
            //服务器断连
            VpnUser user = userMapper.selectById(userId);
            disconnectToServer(tCon.getServerIp(), tCon.getPubKey(), tCon.getAllowip(), user.getUsername());
            //删除数据库连接
            tCon.setStatus(0);
            tCon.setUpdateTime(new Date());
            vpnConnectionMapper.updateById(tCon);
            //更新allowIp状态为可用
            VpnAllowedip tip = allowIpMapper.selectById(tCon.getAllowipId());
            if(tip != null) {
                tip.setStatus(1);
                allowIpMapper.updateById(tip);
            }
        }
    }

    @Transactional
    public void disconnectOfDevice(VpnConnection tCon, String userId) {
            VpnUser user = userMapper.selectById(userId);
            disconnectToServer(tCon.getServerIp(), tCon.getPubKey(), tCon.getAllowip(), user.getUsername());
            //更新数据库连接状态
            tCon.setStatus(0);
            tCon.setUpdateTime(new Date());
            vpnConnectionMapper.updateById(tCon);
            //更新allowIp状态为可用
            VpnAllowedip tip = allowIpMapper.selectById(tCon.getAllowipId());
            if(tip != null) {
                tip.setStatus(1);
                allowIpMapper.updateById(tip);
            }
    }

    @Override
    @Transactional
    public Object disconnect() {
        String token = request.getHeader("Authorization");
        String userIdString = jwtTokenUtil.getUsernameFromToken(token);
        String deviceIdentify = request.getHeader("device");
        disconnect(deviceIdentify, userIdString);
        return "断连成功";
    }

    @Override
    public Object getOrderedServers() {
        String lang = request.getHeader("language");
        String token = request.getHeader("Authorization");
        String[] myServerId = jwtTokenUtil.getServerId(token);
        List<VpnServer> servers = serverMapper.getOrderedServers();
        if(myServerId != null && myServerId.length > 0) {
            servers = servers.stream().filter(e -> e.getIsFree() == 0).collect(Collectors.toList());
            for (String s : myServerId) {
                VpnServer server = serverMapper.selectById(Integer.valueOf(s));
                servers.add(0, server);
            }
        }
        if (servers == null || servers.size() == 0) {
            return null;
        }
        for(VpnServer server: servers) {
            if(lang.equals("1")) {
                server.setServerName(server.getEnglishName());
            } else if(lang.equals("2")) {
                server.setServerName(server.getTwName());
            }
        }
        return servers;
    }

    @Override
    public List<VpnServer> getServers() {
        String lang = request.getHeader("language");
        VpnServer qs = new VpnServer();
        qs.setStatus(1);
        QueryWrapper<VpnServer> queryWrapper = new QueryWrapper<VpnServer>(qs);
        List<VpnServer> servers = serverMapper.selectList(queryWrapper);
        if(servers == null || servers.size() == 0) {
            return null;
        }
        for(VpnServer server: servers) {
            if(lang.equals("1")) {
                server.setServerName(server.getEnglishName());
            } else if(lang.equals("2")) {
                server.setServerName(server.getTwName());
            }
        }
        return servers;
    }

    /**
     * @Description: 开启连接vpn服务器配置
     * @Author: Mr. Stone
     */
    public void connectToServer(String serverIp, String publicKey, String allowIp, String username) throws VpnException{
        configVpnToServer(serverIp, publicKey, allowIp, username, "allowed-ips");
    }

    /**
    * @Description: 撤销vpn服务器配置
    * @Author: Mr. Stone
    */
    public void disconnectToServer(String serverIp, String publicKey, String allowIp, String username) throws VpnException{
         configVpnToServer(serverIp, publicKey, allowIp, username, "remove");
    }

    /**
     * 配置到服务器
     * */
    public void configVpnToServer(String serverIp,
                                    String publicKey,
                                    String allowIp,
                                    String username,
                                    String action) throws VpnException{
        RestTemplate restTemplate = new RestTemplate();
        String url = "http://" + serverIp + ":55699";
        logger.info("upload config to vpnserver: {}", url);
        logger.info("upload config\n serverIp: {}\npk:{}\nallowIp:{}\nusername:{}\naction:{}",serverIp, publicKey,allowIp, username, action);
        try {
            MultiValueMap<String, String> header = new LinkedMultiValueMap<>();
            header.add(HttpHeaders.CONTENT_TYPE, (MediaType.APPLICATION_FORM_URLENCODED_VALUE));
            MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
            map.add("cli_pub", publicKey);
            map.add("allowed_ip", allowIp);
            map.add("action", action);
            map.add("id", username);
            HttpEntity<String> request = new HttpEntity(map, header);
            ResponseEntity<String> result = restTemplate.exchange(url, HttpMethod.POST, request, String.class);
            logger.info("vpnServerMessage: {}",result.getBody());
            JSONObject ro =  JSON.parseObject(result.getBody());
            Integer code = (Integer)ro.get("code");
            if(code != 0) {
                logger.info("config error: {}", ro.get("msg"));
                if(!action.equals("remove")) {
                    throw new VpnException(BusinessEnum.VPN_CONFIG_ERROR);
                }
            }
        } catch (Exception e) {
            if(!action.equals("remove")) {
                throw new VpnException(BusinessEnum.VPN_CONFIG_ERROR);
            }
            logger.info("config error: {}", e.getMessage());
        }
    }

    // 用户最后购买的套餐
    public Object getLastPackage(String userId) {
        String lang = request.getHeader("language");
        AvailablePackage lPackage = userPackageMapper.findLastUnusedOrInusePackage(new Integer(userId));
        if(lPackage == null) {
            return null;
        }
        if(lang.equals("1")) {
            lPackage.setPackageName(lPackage.getEnglishName());
        } else if(lang.equals("2")) {
            lPackage.setPackageName(lPackage.getTaiwanName());
        }
        return lPackage;
    }

    // 用户当前套餐
    public Object getCurrentPackage(String userId) {
        String lang = request.getHeader("language");
        AvailablePackage lPackage = userPackageMapper.findCurrentPakcage(new Integer(userId));
        if(lPackage == null) {
            return null;
        }
        if(lang.equals("1")) {
            lPackage.setPackageName(lPackage.getEnglishName());
        } else if(lang.equals("2")) {
            lPackage.setPackageName(lPackage.getTaiwanName());
        }
        return lPackage;
    }


    // 获取所有购买过的套餐
    public Object getHistoryPackage(String userId) {
        String lang = request.getHeader("language");
        List<AvailablePackage> packages = userPackageMapper.findHistoryPackages(new Integer(userId));
        if(packages == null || packages.size() == 0) {
            return null;
        }
        packages = packages.stream()
                .filter((pack) -> pack.getPrice() != 0).map((pack) -> {
            Date now = new Date();
            Date startTime = pack.getStartTime();
            Date endTime = pack.getEndTime();
            if(endTime.compareTo(now) < 0) {
                pack.setStatus(0);
            } else if(now.compareTo(startTime) < 0) {
                pack.setStatus(2);
            } else {
                pack.setStatus(1);
            }
            if(lang.equals("1")) {
                pack.setPackageName(pack.getEnglishName());
            } else if(lang.equals("2")) {
                pack.setPackageName(pack.getTaiwanName());
            }
            return pack;
        }).collect(Collectors.toList());
        return packages.size() == 0 ? null : packages;
    }
}
